import {
  openPopup as larkOpenPopup,
  draw,
  clearDrawGeometry,
  FeatureLayer,
  getMapViewState,
  setMapViewState,
  Circle,
  highlightByGeometry,
  removeHighlight,
  planarArea
} from 'lark-map'
import {isArr} from "@/utils/arrayUtil";
import {endLoading, startLoading} from "@/utils/loadingUtil";

/**
 * 地图控制类
 */
class BaseMap {

  view = null;

  // 监听列表
  monitorList = [];

  getPopupTemplate = null;

  layerFiledInfo = {}

  constructor(view, getPopupTemplate) {
    this.view = view;
    this.getPopupTemplate = getPopupTemplate
    this.onClickFun()
  }


  get view() {
    return this.view;
  }

  /**
   *  绘 制
   * @param type                  绘制类型
   * @param clearGeometry         是否清除绘制物
   * @returns {Promise<unknown>}
   */
  drawGeometry(type = 'polygon', clearGeometry = false) {
    let {view} = this
    return new Promise(async (resolve, reject) => {
      setMapViewState('occupied');
      let graphic = await draw({type, view});
      if (!graphic?.geometry) {
        setMapViewState('unoccupied');
        reject()
        return
      }
      clearGeometry && clearDrawGeometry({view});
      setMapViewState('unoccupied');
      resolve(graphic)
    })
  }

  /**
   * 添加图层
   * @param source                数据源
   * @param fields                字段
   * @param layerId               图层id
   * @param rendererSymbol        渲染renderer
   * @param visible               是否显示
   * @param opacity               透明度
   * @param geometryType          空间数据类型
   * @param txtFileList          标注字段
   * @param txtColor              文字颜色
   * @param fontSize              文字大小
   * @returns {*}
   */
  addFeatureLayer({
                    source,
                    fields,
                    layerId,
                    rendererSymbol,
                    visible = true,
                    opacity = 1,
                    geometryType = 'polygon',
                    txtFileList,
                    txtColor = '#000',
                    fontSize = 16
                  }) {
    fields = fields?.length ? fields : [];
    if (isArr(txtFileList)) {
      this.layerFiledInfo[layerId] = _.cloneDeep(txtFileList)
    }
    fields.unshift({
      name: 'OBJECTID', alias: 'OBJECTID', type: 'oid'
    });
    let symbol = rendererSymbol ? rendererSymbol : {
      type: 'simple-fill', color: [255, 255, 255, 0], outline: {
        color: [255, 255, 0, 0.9], width: 3
      }
    }

    let txt = ''
    let mapTxtList = txtFileList.filter(item => item.addToMap == null || item.addToMap)
    if (isArr(mapTxtList)) {
      _.forEach(mapTxtList, (item, index) => {
        let {prefix, suffix, value} = item
        prefix = prefix && prefix !== '' ? prefix : ''
        suffix = suffix && suffix !== '' ? suffix : ''
        txt = `${index == 0 ? '[' : ''}${txt}${prefix !== '' ? `"${prefix}" + ` : ''}$feature.${value}${suffix !== '' ? `+ "${suffix}"` : ''}${index !== txtFileList.length - 1 ? ',' : ']'}`
      })
    }

    const expressionLabelTxt = txt == '' ? '' : `Concatenate(${txt}, TextFormatting.NewLine)`
    let layer = new FeatureLayer({
      id: layerId,
      fields,
      objectIdField: 'OBJECTID',
      geometryType,
      source,
      visible,
      opacity,
      spatialReference: {wkid: 4490},
      renderer: {
        type: 'simple', symbol
      },
      labelingInfo: [{
        labelExpressionInfo: {expression: expressionLabelTxt,},
        labelPlacement: "center-right",
        maxScale: 0,
        minScale: 25000000,
        symbol: {
          type: "text",
          font: {
            size: fontSize,
            family: "Noto Sans"
          },
          horizontalAlignment: "left",
          color: txtColor
        }
      }]
    })
    this.view.map.add(layer)
    return layer
  }

  /**
   * 删除数据
   * @param features
   * @param layer
   * @returns {Promise<unknown>}
   */
  delFeature({features, layerId}) {
    let owner = this
    return new Promise((resolve, reject) => {
      let layer = owner.findLayerById(layerId)
      if (!features?.length || !layer) {
        reject()
      } else {
        layer.applyEdits({deleteFeatures: features}).then(res => {
          resolve()
        });
      }
    });
  }

  /**
   *  移除一个图层
   * @param layerId
   */
  removeFeatureLayer({layerId}) {
    let layer = this.findLayerById(layerId)
    if (layer) {
      this.view.map.remove(layer)
    }
  }

  /**
   * 根据图层id获取图层
   * @param id
   * @returns {*}
   */
  findLayerById(id) {
    return this.view.map.findLayerById(id)
  }

  /**
   * 向featureLayer中添加数据
   * @param layerId                 图层id
   * @param features                数据源
   * @returns {Promise<unknown>}
   */
  addFeature(layerId, features) {
    let owner = this
    return new Promise((resolve, reject) => {
      const layer = owner.findLayerById(layerId)
      if (!features?.length || !layer) {
        reject()
      } else {
        layer.applyEdits({addFeatures: features}).then(res => {
          resolve()
        })
      }
    })
  }

  /**
   * 检查图层是否已开启监听
   * @param layerId
   * @returns {boolean}
   */
  checkMonitorByLayerId({layerId, backFun}) {
    let currentItem = this.monitorList.find(item => item.layerId === layerId)
    if (!currentItem) return false;
    return currentItem.backFunList.some(item => item === backFun)
  }

  /**
   * 添加点击事件的监听
   * @param layerId     图层id
   * @param backFun     回调函数
   */
  addMonitorByLayerId({layerId, backFun}) {
    if (!this.monitorList.some(item => item.layerId === layerId)) {
      this.monitorList.push({layerId, backFunList: []})
    }
    _.map(this.monitorList, item => {
      if (item.layerId === layerId) {
        item.backFunList.push(backFun)
      }
    })
  }

  /**
   * 移除监听
   * @param layerId
   */
  removeMonitorByLayerId({layerId, backFun}) {
    if (!this.monitorList.some(item => item.layerId === layerId)) return;
    _.map(this.monitorList, item => {
      if (item.layerId === layerId) {
        item.backFunList = _.filter(item.backFunList, funItem => funItem != backFun)
      }
    });
    this.monitorList = _.filter(this.monitorList, monitorItem => isArr(monitorItem.backFunList))
  }

  /**
   * 开启地图点击事件的监听
   */
  onClickFun() {
    let {view} = this
    let owner = this
    view.on("click", async event => {
      if (getMapViewState() === 'occupied' || !isArr(owner.monitorList)) return;
      owner.removeHighlight()
      let {mapPoint: geometry} = event;
      let queryGeometry = new Circle({
        spatialReference: geometry.spatialReference,
        center: geometry,
        radius: 1000 * view.resolution,
        radiusUnit: 'miles'
      })
      for (let i = 0; i < owner.monitorList.length; i++) {
        let {layerId, backFunList} = owner.monitorList[i];
        if (!isArr(backFunList)) return;
        const layer = owner.findLayerById(layerId);
        let queryRes = await owner.queryFeature({layer, geometry: queryGeometry});
        if (isArr(queryRes.features)) {
          _.forEach(backFunList, backFun => {
            backFun({point: geometry, layer, features: queryRes.features})
          })
        }
      }
    });
  }

  /**
   * 查询feature
   * @param layer                           被查询的图层
   * @param geometry                        空间参数
   * @param where                           属性参数
   * @returns {Promise<unknown>}
   */
  queryFeature({layer = null, geometry = null, where = '1 = 1'}) {
    let {view} = this
    return new Promise((resolve, reject) => {
      startLoading()
      if (!view || !layer) {
        endLoading()
        reject()
      } else {
        let query = layer.createQuery();
        if (geometry) {
          query.geometry = geometry;
        }
        query.where = where;
        query.returnGeometry = true;
        query.outFields = ['*']
        layer.queryFeatures(query).then(res => {
          endLoading()
          resolve(res)
        });
      }
    });
  }

  /**
   * 检查是否有重合部分
   * @param layerId
   * @param graphic
   * @returns {Promise<unknown>}
   */
  checkCoincideByGraphic({layerId, graphic}) {
    let owner = this
    return new Promise(async (resolve, reject) => {
      let layer = owner.findLayerById(layerId);
      if (!layer || !graphic || !graphic.geometry) {
        resolve(false);
        return
      }
      let res = await owner.queryFeature({layer, geometry: graphic.geometry})
      resolve(res?.features?.length > 0);
    })
  }

  /**
   * 打开弹框
   * @param dataList
   * @param location
   * @returns {Promise<void>}
   */
  async openPopup(layerId,features, location, popupTitle) {
    let dataItem = features[0].attributes
    let {layerFiledInfo} = this;
    const dataList = []
    if (isArr(layerFiledInfo[layerId])) {
      layerFiledInfo[layerId].forEach(item => {
        let {label, value} = item
        dataList.push({label, value: dataItem[value]})
      })
    }
    let popupContent = await this.getPopupTemplate(_.cloneDeep(dataList), popupTitle)
    let {view} = this
    larkOpenPopup({view, location, popupContent, zoom: false})
  }

  /**
   * 高亮
   * @param geometry
   */
  highlight(geometry) {
    let {view} = this
    highlightByGeometry({view, geometry})
  }

  /**
   * 高亮
   * @param geometry
   */
  removeHighlight() {
    let {view} = this
    removeHighlight(view)
  }


  /**
   * 定位
   * @param geometry
   * @returns {Promise<unknown>}
   */
  async goToGeometry(geometry) {
    return new Promise(async (resolve, reject) => {
      let {type, extent} = geometry
      let locationGeometry = type === 'point' ? new Circle({
        spatialReference: geometry.spatialReference,
        center: geometry,
        radius: 500 * this.view.resolution,
        radiusUnit: 'miles'
      }) : geometry
      await this.view.goTo(locationGeometry)
      resolve()
    })
  }

  /**
   * 关闭弹框
   */
  closePopup() {
    if (this.view.popup) {
      this.view.popup.close()
    }
  }

  /**
   * 计算面积
   * @param geometryList
   * @returns {{areaList: *[], sumArea: number}}
   */
  getAreaByGeometryList(geometryList) {
    const areaList = [];
    let sumArea = 0;

    if (!isArr(geometryList)) {
      return {areaList, sumArea}
    }
    let owner = this
    _.forEach(geometryList, geometry => {
      let area = owner.getAreaByGeometry(geometry)
      areaList.push({geometry, area})
      sumArea += area;
    })
    return {areaList, sumArea: _.round(sumArea, 2)}
  }

  /**
   * 计算面积
   * @param geometry
   * @returns {number|number}
   */
  getAreaByGeometry(geometry) {
    return geometry ? _.round(planarArea({geometry}) * 10000 * 1500, 2) : 0
  }
}

export {BaseMap}
